package com.zh.sharding.sale.service;

import com.zh.mycommon.util.MyIdGenerator;
import com.zh.sharding.sale.entity.MyOrders;
import com.zh.sharding.sale.entity.Product;
import com.zh.sharding.sale.repository.OrdersDao;
import com.zh.sharding.sale.repository.ProductDao;
import lombok.AllArgsConstructor;
import org.apache.shardingsphere.sharding.algorithm.keygen.SnowflakeKeyGenerateAlgorithm;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author ZH
 * @date 09:20 2023/8/4
 */
@AllArgsConstructor
@Service
public class OrderService {
    static KeyGenerateAlgorithm idGenerator  = new SnowflakeKeyGenerateAlgorithm();
    static AtomicInteger adder = new AtomicInteger();

    private ProductDao productDao;
    private OrdersDao ordersDao;
    private void autoOrderNo(MyOrders orders){
        String autoId = "SD"+idGenerator.generateKey().toString();
        orders.setNo(autoId);
    }
    /**
     * 下单-不安全
     * unsafety
     * @param orders
     * @return
     */
    @Transactional
    public String createOrder(MyOrders orders) {
        String reqTag = "[" + Thread.currentThread().getName() + "] [" + adder.incrementAndGet() + "]\t";
        mockBlock();

        Product product = productDao.selectById(orders.getProductId());
        mockBlock();

        int store = product.getStore();
        System.out.println(reqTag + String.format("查询到的商品[%s]库存有：%d", product.getName(), store));

        if (store > 0) {
            // 扣库存
            if (productDao.updateStoreById(product.getId(), store - 1) == 0) {
                throw new RuntimeException("扣减库存未成功");
            }
            // 添加订单
            autoOrderNo(orders);
            ordersDao.insert(orders);

            return orders.getNo();
        } else {
            System.err.println(reqTag + String.format("商品[%s]库存不够啦！", product.getName()));
            throw new RuntimeException("库存不足，无法下单");
        }
    }

    /**
     * 下单
     * safety
     * @param orders
     * @return
     */
    @Transactional
    public String createOrder_lock(MyOrders orders) {
        String reqTag = "[" + Thread.currentThread().getName() + "] [" + adder.incrementAndGet() + "]\t";
        mockBlock();

        Product product = productDao.selectByIdLock(orders.getProductId());
        mockBlock();

        int store = product.getStore();
        System.out.println(reqTag + String.format("查询到的商品[%s]库存有：%d", product.getName(), store));

        if (store > 0) {
            // 扣库存
            if (productDao.updateStoreById(product.getId(), store - 1) == 0) {
                throw new RuntimeException("扣减库存未成功");
            }
            // 添加订单
            autoOrderNo(orders);
            calculateAmount(orders,product);
            ordersDao.insert(orders);

            return orders.getNo();
        } else {
            System.err.println(reqTag + String.format("商品[%s]库存不够啦！", product.getName()));
            throw new RuntimeException("库存不足，无法下单");
        }
    }


    /**
     * 下单
     * safety
     * @param orders
     * @return
     */
    @Transactional
    public String createOrder_new(MyOrders orders) {
        String reqTag = "[" + Thread.currentThread().getName() + "] [" + adder.incrementAndGet() + "]\t";
        mockBlock();

        Product product = productDao.selectById(orders.getProductId());

        mockBlock();

        int store = product.getStore();
        System.out.println(reqTag + String.format("查询到的商品[%s]库存有：%d", product.getName(), store));

        // 扣库存
        if (productDao.deduceStoreById(product.getId(), 1) == 0) {
            System.err.println(reqTag + String.format("商品[%s]库存不够啦！", product.getName()));
            throw new RuntimeException("扣减库存失败，库存不足，无法下单");
        }

        // 添加订单
        autoOrderNo(orders);
        calculateAmount(orders,product);
        ordersDao.insert(orders);
        return orders.getNo();
    }

    private void calculateAmount(MyOrders orders, Product product) {
        BigDecimal amountPrice = product.getPrice().multiply(BigDecimal.valueOf(orders.getNum()))
                .setScale(2, RoundingMode.HALF_UP);
        orders.setAmount(amountPrice);
    }

    private static void mockBlock(){
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
